package com.baidu.iit.pxp.handler;

import com.baidu.iit.pvm.ActivityException;
import com.baidu.iit.pvm.process.ActivityImpl;
import com.baidu.iit.pvm.process.ScopeImpl;
import com.baidu.iit.pxp.behavior.EventSubProcessStartEventActivityBehavior;
import com.baidu.iit.pxp.constants.BpmnXMLConstants;
import com.baidu.iit.pxp.entity.ProcessDefinitionEntity;
import com.baidu.iit.pxp.event.StartEvent;
import com.baidu.iit.pxp.model.BaseElement;
import com.baidu.iit.pxp.model.ErrorEventDefinition;
import com.baidu.iit.pxp.model.EventDefinition;
import com.baidu.iit.pxp.model.SignalEventDefinition;
import com.baidu.iit.pxp.parser.BpmnParse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * User: huangweili
 * Date: 14-4-29
 * Time: 下午12:56
 */
public class StartEventParseHandler extends AbstractActivityBpmnParseHandler<StartEvent> {

    private static Logger logger = LoggerFactory.getLogger(StartEventParseHandler.class);

    public static final String PROPERTYNAME_INITIATOR_VARIABLE_NAME = "initiatorVariableName";
    public static final String PROPERTYNAME_INITIAL = "initial";

    @Override
    public Class<? extends BaseElement> getHandledType() {
        return StartEvent.class;
    }

    @Override
    protected void executeParse(BpmnParse bpmnParse, StartEvent startEvent) {
        ActivityImpl startEventActivity = createActivityOnCurrentScope(bpmnParse, startEvent, BpmnXMLConstants.ELEMENT_EVENT_START);

        ScopeImpl scope = bpmnParse.getCurrentScope();
        if (scope instanceof ProcessDefinitionEntity) {
            createProcessDefinitionStartEvent(bpmnParse, startEventActivity, startEvent, (ProcessDefinitionEntity) scope);
            selectInitial(bpmnParse, startEventActivity, startEvent, (ProcessDefinitionEntity) scope);
        } else {
            createScopeStartEvent(bpmnParse, startEventActivity, startEvent);
        }
    }

    protected void selectInitial(BpmnParse bpmnParse, ActivityImpl startEventActivity, StartEvent startEvent, ProcessDefinitionEntity processDefinition) {
        if (processDefinition.getInitial() == null) {
            processDefinition.setInitial(startEventActivity);
        } else {
            // validate that there is a single none start event / timer start event:
            if (!startEventActivity.getProperty("type").equals("messageStartEvent")) {
                String currentInitialType = (String) processDefinition.getInitial().getProperty("type");
                if (currentInitialType.equals("messageStartEvent")) {
                    processDefinition.setInitial(startEventActivity);
                } else {
                    throw new ActivityException("multiple none start events or timer start events not supported on process definition");
                }
            }
        }
    }

    protected void createProcessDefinitionStartEvent(BpmnParse bpmnParse, ActivityImpl startEventActivity, StartEvent startEvent, ProcessDefinitionEntity processDefinition) {
        if (StringUtils.isNotEmpty(startEvent.getInitiator())) {
            processDefinition.setProperty(PROPERTYNAME_INITIATOR_VARIABLE_NAME, startEvent.getInitiator());
        }


        startEventActivity.setActivityBehavior(bpmnParse.getActivityBehaviorFactory().createNoneStartEventActivityBehavior(startEvent));
        if (startEvent.getEventDefinitions().size() > 0) {
            EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0);
            if (eventDefinition instanceof SignalEventDefinition) {
                bpmnParse.getBpmnParserHandlers().parseElement(bpmnParse, eventDefinition);
            } else {
                logger.warn("Unsupported event definition on start event", eventDefinition);
            }
        }
    }

    protected void createScopeStartEvent(BpmnParse bpmnParse, ActivityImpl startEventActivity, StartEvent startEvent) {

        ScopeImpl scope = bpmnParse.getCurrentScope();
        Object triggeredByEvent = scope.getProperty("triggeredByEvent");
        boolean isTriggeredByEvent = triggeredByEvent != null && ((Boolean) triggeredByEvent == true);

        if (isTriggeredByEvent) {
            EventSubProcessStartEventActivityBehavior activityBehavior =
                    bpmnParse.getActivityBehaviorFactory().createEventSubProcessStartEventActivityBehavior(startEvent, startEventActivity.getId());
            startEventActivity.setActivityBehavior(activityBehavior);

            if (startEvent.getEventDefinitions().size() > 0) {
                EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0);

                if (eventDefinition instanceof ErrorEventDefinition || eventDefinition instanceof SignalEventDefinition) {
                    bpmnParse.getBpmnParserHandlers().parseElement(bpmnParse, eventDefinition);
                } else {
                    logger.warn("start event of event subprocess must be of type 'error', 'message' or 'signal' for start event " + startEvent.getId());
                }
            }

        } else {

            if (startEvent.getEventDefinitions().size() > 0) {
                logger.warn("event definitions only allowed on start event if subprocess is an event subprocess " + bpmnParse.getCurrentSubProcess().getId());
            }
            if (scope.getProperty(PROPERTYNAME_INITIAL) == null) {
                scope.setProperty(PROPERTYNAME_INITIAL, startEventActivity);
                startEventActivity.setActivityBehavior(bpmnParse.getActivityBehaviorFactory().createNoneStartEventActivityBehavior(startEvent));
            } else {
                logger.warn("multiple start events not supported for subprocess", bpmnParse.getCurrentSubProcess().getId());
            }
        }

    }

}
